package loads

import (
	"jvm/instructions/base"
	"jvm/rtda"
	"jvm/rtda/heap"
	. "jvm/typedef"
)

// **************************************************
// 检查空指针
func checkNotNil(ref *heap.Object) {
	if ref == nil {
		panic("java.lang.NullPointerException")
	}
}

// 检查下标是否越界
func checkIndex(arrLen I4, index I4) {
	if index < 0 || index >= arrLen {
		panic("ArrayIndexOutOfBoundsException")
	}
}

// **************************************************
type AALOAD struct {
	base.NoOperandsInst
}

func (self *AALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	refs := arrRef.Refs()
	checkIndex(I4(len(refs)), index)
	stack.PushRef(refs[index])
}

// **************************************************
type BALOAD struct {
	base.NoOperandsInst
}

func (self *BALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	bytes := arrRef.Bytes()
	checkIndex(I4(len(bytes)), index)
	stack.PushInt(I4(bytes[index]))
}

// **************************************************
type SALOAD struct {
	base.NoOperandsInst
}

func (self *SALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	shorts := arrRef.Shorts()
	checkIndex(I4(len(shorts)), index)
	stack.PushInt(I4(shorts[index]))
}

// **************************************************
type CALOAD struct {
	base.NoOperandsInst
}

func (self *CALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	chars := arrRef.Chars()
	checkIndex(I4(len(chars)), index)
	stack.PushInt(I4(chars[index]))
}

// **************************************************
type IALOAD struct {
	base.NoOperandsInst
}

func (self *IALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	ints := arrRef.Ints()
	checkIndex(I4(len(ints)), index)
	stack.PushInt(ints[index])
}

// **************************************************
type LALOAD struct {
	base.NoOperandsInst
}

func (self *LALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	longs := arrRef.Longs()
	checkIndex(I4(len(longs)), index)
	stack.PushLong(longs[index])
}

// **************************************************
type FALOAD struct {
	base.NoOperandsInst
}

func (self *FALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	floats := arrRef.Floats()
	checkIndex(I4(len(floats)), index)
	stack.PushFloat(floats[index])
}

// **************************************************
type DALOAD struct {
	base.NoOperandsInst
}

func (self *DALOAD) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	index := stack.PopInt()
	arrRef := stack.PopRef()
	checkNotNil(arrRef)
	doubles := arrRef.Doubles()
	checkIndex(I4(len(doubles)), index)
	stack.PushDouble(doubles[index])
}
